/*
 *  FILE: 
 *  	portable_socket.c
 *
 *  DESCRIPTION:
 *  	Opaque set of functions for (non-)blocking sockets using
 *      encryption or plaintext, with full support for systems such as Win32.  
 *      Also included are simple replacements for many Unix functions dealing with sockets.
 *
 *  FUNCTIONS:
 *      sock_nonblock() - Set socket fd to non-blocking mode. (BSD sockets only)
 *      sock_block()    - Set socket fd to blocking mode. (BSD sockets only)
 *      socketpair()    - Very simple rewrite of socketpair on Win32 for 
 *                        AF_LOCAL/SOCK_STREAM connection, with no options.
 *                        Call psocket_init() if problems with binary data occur.
 *      mysendfile()    - Linux sendfile() replacement; Is User-Space...
 *      psocket_init()  - SIGPIPE ignore for *n*x, and winsock/_fmode stuff on Windows.
 *                        Also includes OpenSSL init functions.
 *                        Please Note: this function on windows changes all pipes to binary.
 *                        If binary data becomes corrupted via socketpair, it is because the
 *                        pipes are no longer binary...
 *                        Run at the beginning of your program.
 *  	readline_r()    - Reentrant readline function with support for CRLF, 
 *                        or just CR.
 *  	fullsend()      - Write data without worrying about all of it being written.  
 *                        Useful for sockets.
 *
 * 
 *
 * Copyright (c) 2004-2006 Karlan Thomas Mitchell<karlanmitchell@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *         This product includes software developed by
           Karlan Thomas Mitchell<karlanmitchell@comcast.net>
 * 4. Neither Karlan Thomas Mitchell nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY KARLAN THOMAS MITCHELL AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
*/
#include "portable_socket.h"


/*Set socket to non-blocking*/
int sock_nonblock(int sock){
#ifdef __WIN32__
	unsigned long cmd = 1;
	if ( ioctlsocket(sock, FIONBIO, &cmd)  <  0 ){
		perror("socketioctl");
		return -1;
		}
#else
	int cmd = fcntl(sock, F_GETFL);
	if ( cmd < 0 ){
		perror("fcntl");
		return -1;
		}
	if ( fcntl(sock, F_SETFL, cmd | O_NONBLOCK) < 0 ){
		perror("fcntl");
		return -1;
		}
#endif
	return 0;
}

/*Set socket to blocking*/
int sock_block(int sock){
#ifdef __WIN32__
	unsigned long cmd = 0;
	if ( ioctlsocket(sock, FIONBIO, &cmd)  <  0 ){
		perror("socketioctl");
		return -1;
		}
#else
	int cmd = fcntl(sock, F_GETFL);
	if ( cmd < 0 ){
		perror("fcntl");
		return -1;
		}
	if ( fcntl(sock, F_SETFL, cmd &~O_NONBLOCK) < 0 ){
		perror("fcntl");
		return -1;
		}
#endif
	return 0;
}




#ifdef __WIN32__
#ifndef __CYGWIN__

/*socketpair for windows, remember to run psocket_init()*/
int socketpair(int domain, int type, int protocol, int sv[2]){

    if(protocol != 0){
	            errno = EINVAL;
	return -1;
	}
    if(type != SOCK_STREAM){
	        errno = EINVAL;
	return -1;
	}
    if(domain != AF_LOCAL){
	          errno = EINVAL;
	return -1;
	}

    return _pipe(sv, 4096, _O_BINARY );
}


#endif /*!CYGWIN*/
#endif /*Win32*/


off_t mysendfile(int out_fd, FILE * in_fd, off_t *offset, off_t count){
	off_t cur_count = 0;
	int block_size = 1024;
	int buffer[block_size];
	ssize_t return_val;
	fseek(in_fd, *offset, SEEK_SET);
	while (cur_count < count){
		
		while(cur_count + block_size > count && block_size > 1)
			{block_size--;}
		if(cur_count + block_size > count)
			break;

		return_val = fread(buffer, 1, block_size, in_fd);		


		send(out_fd, (void *)buffer, return_val, 0);

		cur_count += return_val;
		}
	return cur_count;
	}


void psocket_zero(struct st_psocket * socket_data){
        memset(socket_data, '\0', sizeof(struct st_psocket));
	}

int psocket_init(){
	
	#ifdef USE_OPENSSL
	/* Initializing OpenSSL */
	SSL_load_error_strings();
	ERR_load_BIO_strings();
	OpenSSL_add_all_algorithms();
	#endif


	#ifndef __WIN32__
	/*UNIX*/
	/*Don't kill program on SIGPIPE!*/
	signal(SIGPIPE, SIG_IGN);

	
	#else
	/*Microsoft Windows Code: */

	/*for socketpair*/
        _fmode = _O_BINARY;
        WSADATA wsaData; 

	/*Winsock*/
       if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
              fprintf(stderr, "WSAStartup failed.\n");
              return -1;
       } 
	#endif
	return 0;
}





#ifdef USE_OPENSSL


ssize_t BIO_fullsend(BIO * bio, char * buffer, ssize_t bufferlen){
	return core_fullsend((void *)bio, buffer, bufferlen, MODE_BIO);
}
ssize_t BIO_fullread(BIO * bio, char * buffer, ssize_t bufferlen, ssize_t readlen){
	return core_fullread_timeout((void *)bio, buffer, bufferlen, readlen, MODE_BIO, NULL);
}

ssize_t BIO_readline_r(BIO * bio, char * buffer, ssize_t bufferlen){
	return core_readline_r_timeout((void *)bio, buffer, bufferlen, MODE_BIO, NULL);
}
ssize_t BIO_readline_r_timeout(BIO * bio, char * buffer, ssize_t bufferlen, struct st_timeval * time_data){
	return core_readline_r_timeout((void *)bio, buffer, bufferlen, MODE_BIO, time_data);
}
#endif


ssize_t freadline_r(FILE * fd, char * buffer, ssize_t bufferlen){
	return core_readline_r_timeout((void *)fd, buffer, bufferlen, MODE_FILE, NULL);
}

ssize_t readline_r(int fd, char * buffer, ssize_t bufferlen, int read_socket){
	if(read_socket)
		return core_readline_r_timeout((void *)fd, buffer, bufferlen, MODE_RECV, NULL);
	else
		return core_readline_r_timeout((void *)fd, buffer, bufferlen, MODE_READ, NULL);
}

ssize_t fullread(int fd, char * buffer, size_t bufferlen, size_t readlen, int read_socket){
	if(read_socket)
		return core_fullread_timeout((void *)fd, buffer, bufferlen, readlen, MODE_RECV, NULL);
	else
		return core_fullread_timeout((void *)fd, buffer, bufferlen, readlen, MODE_READ, NULL);
}


/*To be used with non-blocking sockets...*/
ssize_t readline_r_timeout(int fd, char * buffer, ssize_t bufferlen, int read_socket, struct st_timeval * time_data){
	if(read_socket)
		return core_readline_r_timeout((void *)fd, buffer, bufferlen, MODE_RECV, time_data);
	else
		return core_readline_r_timeout((void *)fd, buffer, bufferlen, MODE_READ, time_data);
}


/*WARNING: THIS FUNCTION SENDS UP TO *(buffer+buflen), zero stops nothing!
  NOTE: The reason for above: the ability to send binary data*/
ssize_t core_fullsend(void * fd, char * buffer, ssize_t bufferlen, int socket_type){

	if(bufferlen <= 0)
		return -1;
	ssize_t left = bufferlen;
	ssize_t sent;
	char * sendpnt= &(buffer[0]);
	
	while(left > 0){
		if(socket_type == MODE_SEND)
		   	sent = send((int)fd, sendpnt, left, 0);
		else if(socket_type == MODE_WRITE)
	   	   	sent = write((int)fd, sendpnt, left);
		#ifdef USE_OPENSSL
		else if(socket_type == MODE_BIO)
			sent = BIO_write((BIO *)fd, sendpnt, left);
		#endif
		else if(socket_type == MODE_FILE)
	   	   	sent = fwrite(sendpnt, 1, left, (FILE *)fd);
		else
			return -2;

		if(sent == 0){
			#ifdef USE_OPENSSL
			if(socket_type == MODE_BIO
                            && BIO_should_retry((BIO *)fd) != 0)
				continue;
			#endif
			return sent;
			}
		if(sent < 0){
			if(get_errno == EWOULDBLOCK || get_errno == EAGAIN){
				continue;
				}
			return -1;
			}


		sendpnt = &(sendpnt[sent]);
		left -= sent;
	}
	return bufferlen - left;
}



/*NOTE: buffer's len should be readlen+1 AT LEAST!!!!!!*/
ssize_t core_fullread_timeout(void * fd, char * buffer, size_t bufferlen, size_t readlen, int socket_type, struct st_timeval * time_data){
	*buffer = 0;

	time_t start_time = time(NULL);
	time_t curr_time;
	int tmp_errno = get_errno;

	if(readlen <= 0 || bufferlen <= 0)
		return -1;

	size_t dataread = 0;
	size_t datacopied = 0;
	size_t to_read = 1023;
	char readbuf[1024];
	ssize_t rv;

	while( 1 ){
		if(time_data != NULL){
 			curr_time = time(NULL);
 			if(curr_time - start_time >= time_data->tv_sec)
 				break;
			}
		if(dataread + to_read >= readlen)
			to_read = readlen - dataread;

		if(to_read <= 0)
			break;

		/*Windows is picky about read vs recv, Unix doesn't care*/
        	
		if(socket_type == MODE_RECV)
		   	rv = recv((int)fd, readbuf, to_read, 0);
		else if(socket_type == MODE_READ)
	   	   	rv = read((int)fd, readbuf, to_read);
		#ifdef USE_OPENSSL
		else if(socket_type == MODE_BIO)
	   	   	rv = BIO_read((BIO *)fd, readbuf, to_read);
		#endif
		else if(socket_type == MODE_FILE || socket_type == MODE_ND_FILE)
	   	   	rv = fread(readbuf, 1, to_read, (FILE *)fd);
		else
			return -2;

		if(rv == 0){
			tmp_errno = get_errno;
			#ifdef USE_OPENSSL
			if(socket_type == MODE_BIO
                            && BIO_should_retry((BIO *)fd) != 0)
				continue;
			#endif
			break;
			}

		
		if(rv < 0){
			tmp_errno = get_errno;
			if(get_errno == EWOULDBLOCK || get_errno == EAGAIN){
				if(time_data != NULL)
					usleep(250000); /*1 quarter of a second*/ 
				continue;
				}
			return -1;
			}


		readbuf[rv] = 0;

		if(datacopied + rv <= bufferlen){
			strlcat(buffer, readbuf, bufferlen);
			datacopied += rv;
			}
		
		dataread += rv;

	}	



	if(time_data != NULL){
		curr_time = time(NULL);
		time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
	}

	errno = tmp_errno;

// 	if(dataread != bufferlen)
// 		return -1;

	return dataread;
}

/*WARNING: If the buffer is too small, data is read up to newline [sequence],
           and data after the size of the buffer is discarded (except for MODE_ND_FILE)*/
/*NOTE: supports \r\n or \n, not just \r*/
/* NOTE: return of zero is empty line */
ssize_t core_readline_r_timeout(void * fd, char * buffer, ssize_t bufferlen, int socket_type, struct st_timeval * time_data){

	time_t start_time = time(NULL);
	time_t curr_time;
	ssize_t return_val;	
	int tmp_errno = get_errno;

	if(bufferlen <= 0)
		return -1;

	ssize_t dataread = 0;
	char tc[2];
	int rv;
	char lastchar = 0;
	int function_return_val = 0;


	while( 1 ){
		if(dataread >= bufferlen - 1){/*Discard Data...*/
			if(socket_type == MODE_ND_FILE)
				break;
			else
				continue;
			}
		if(time_data != NULL){
 			curr_time = time(NULL);
 			if(curr_time - start_time >= time_data->tv_sec)
 				break;
			}

		/*Windows is picky about read vs recv, Unix doesn't care*/
        	
		if(socket_type == MODE_RECV)
		   	rv = recv((int)fd, tc, 1, 0);
		else if(socket_type == MODE_READ)
	   	   	rv = read((int)fd, tc, 1);
		#ifdef USE_OPENSSL
		else if(socket_type == MODE_BIO)
	   	   	rv = BIO_read((BIO *)fd, tc, 1);
		#endif
		else if(socket_type == MODE_FILE || socket_type == MODE_ND_FILE)
	   	   	rv = fread(tc, 1, 1, (FILE *)fd);
		else
			return -2;

//    		printf("X: %c (%d)\n", tc[0], tc[0]);
// 		fflush(stdout);

		if(rv == 0){
			if(socket_type == MODE_FILE || socket_type == MODE_ND_FILE){
				/*File read never? fails, 0 could be end of file*/
				break;
				}
			#ifdef USE_OPENSSL
			if(socket_type == MODE_BIO
                            && BIO_should_retry((BIO *)fd) != 0)
				continue;
			#endif
			function_return_val = -1;
			break;
			}

		
		
		if(rv < 0){
			tmp_errno = get_errno;
			if(get_errno == EWOULDBLOCK || get_errno == EAGAIN){
				if(time_data != NULL)
					usleep(25000); /*1 quarter of a second, divided by 10*/ 
				continue;
				}
			return -1;
			}


		if(tc[0] == '\x0A'){
			/*If CRLF*/
			if(socket_type == MODE_ND_FILE){
				buffer[dataread] = tc[0];
 				dataread++;
			}

			if(lastchar == '\x0D'){
				if(socket_type != MODE_ND_FILE)
					dataread--;
			}
			break;
		}


		buffer[dataread] = tc[0];
		lastchar = tc[0];

		dataread++;

	}	

	/*null it for the programmer,
	  also dataread is set back if CRLF is found to write over CR.*/
	if(dataread < bufferlen)
		buffer[dataread] = 0;


	if(time_data != NULL){
		curr_time = time(NULL);
		time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
	}

	errno = tmp_errno;
	if(function_return_val == -1)
		return function_return_val;

	return dataread;
}

/*

ssize_t core_read_timeout(void * fd, char * buffer, ssize_t bufferlen, int socket_type, struct st_timeval * time_data){

	time_t start_time = time(NULL);
	time_t curr_time = start_time;
	ssize_t return_val;	
	int tmp_errno;


	if(psocket_data->io_method == IO_BSD){
		while(curr_time - start_time < time_data->tv_sec){
			return_val = recv(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 0);
			if(return_val > 0){
				curr_time = time(NULL);
				time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
				return return_val;
				}
			if(return_val == 0){
				tmp_errno = get_errno;
				usleep(250000); /*1 quarter of a second*
				}
			if(return_val < 0)
				break;
		
			curr_time = time(NULL);
		}
	}
	else{
		printf("No OpenSSL psocket_read_timeout\n");
		return -1;
    }

	curr_time = time(NULL);
	time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
	errno = tmp_errno;
	return -1;
}

*/


/*WARNING: THIS FUNCTION SENDS UP TO *(buffer+buflen), zero stops nothing!
  NOTE: The reason for above: the ability to send binary data*/
ssize_t fullsend(int socketd, char * buffer, ssize_t bufferlen){
	return core_fullsend((void *)socketd, buffer, bufferlen, MODE_SEND);
}


ssize_t psocket_setnonblock(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD){
		return sock_nonblock(psocket_data->io_data.bsd_io.clientfd);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_set_nbio(psocket_data->io_data.openssl_io.bio, 1);
	#endif
}
ssize_t psocket_setblock(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD){
		return sock_block(psocket_data->io_data.bsd_io.clientfd);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_set_nbio(psocket_data->io_data.openssl_io.bio, 0);
	#endif
}


ssize_t psocket_setnonblock_server(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD){
		return sock_nonblock(psocket_data->io_data.bsd_io.serverfd);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_set_nbio(psocket_data->io_data.openssl_io.bio, 1);
	#endif
}
ssize_t psocket_setblock_server(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD){
		return sock_block(psocket_data->io_data.bsd_io.serverfd);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_set_nbio(psocket_data->io_data.openssl_io.bio, 0);
	#endif
}

ssize_t psocket_read_timeout(struct st_psocket * psocket_data, char * buffer, size_t bufferlen, struct st_timeval * time_data){
	if(time_data == NULL)
		return psocket_read(psocket_data, buffer, bufferlen);

	time_t start_time = time(NULL);
	time_t curr_time = start_time;
	ssize_t return_val;	
	int tmp_errno;


	if(psocket_data->io_method == IO_BSD){
		while(curr_time - start_time < time_data->tv_sec){
			return_val = recv(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 0);
			if(return_val > 0){
				curr_time = time(NULL);
				time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
				return return_val;
				}
			if(return_val == 0){
				tmp_errno = get_errno;
				usleep(250000); /*1 quarter of a second*/ 
				}
			if(return_val < 0)
				break;
		
			curr_time = time(NULL);
		}
	}
	else{
		printf("No OpenSSL psocket_read_timeout\n");
		return -1;
    }

	curr_time = time(NULL);
	time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
	errno = tmp_errno;
	return -1;
}


ssize_t psocket_write_timeout(struct st_psocket * psocket_data, char * buffer, size_t bufferlen, struct st_timeval * time_data){
	if(time_data == NULL)
		return psocket_write(psocket_data, buffer, bufferlen);

	time_t start_time = time(NULL);
	time_t curr_time  = start_time;
	ssize_t return_val;	
	int tmp_errno;

	if(psocket_data->io_method == IO_BSD){
		while(curr_time - start_time < time_data->tv_sec){
		return_val = send(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 0);
		if(return_val > 0){
			curr_time = time(NULL);
			time_data->tv_sec = time_data->tv_sec - (curr_time - start_time);
			return return_val;
			}
		if(return_val == 0){
			tmp_errno = get_errno;
			usleep(250000); /*1 quarter of a second*/ 
			}
		if(return_val < 0)
			break;

		curr_time = time(NULL);
		}
	}
	else{
		printf("No OpenSSL psocket_read_timeout\n");
		return -1;
    }

	time_data->tv_sec = 0;
	errno = tmp_errno;
	return -1;
}

ssize_t psocket_readline_r_timeout(struct st_psocket * psocket_data, char * buffer, ssize_t bufferlen, struct st_timeval * time_data){

	if(psocket_data->io_method == IO_BSD){
		return readline_r_timeout(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 1, time_data);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_readline_r_timeout(psocket_data->io_data.openssl_io.bio, buffer, bufferlen, time_data);
	#endif
	return -1;
}

ssize_t psocket_fullread(struct st_psocket * psocket_data, char * buffer, size_t bufferlen, size_t readlen){

	if(psocket_data->io_method == IO_BSD){
		return fullread(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, readlen, 1);
	}
	#ifdef USE_OPENSSL
	else
		return BIO_fullread(psocket_data->io_data.openssl_io.bio, buffer, bufferlen, readlen);
	#endif
	return -1;
}

ssize_t psocket_read(struct st_psocket * psocket_data, char * buffer, size_t bufferlen){
	if(psocket_data->io_method == IO_BSD){
		return recv(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 0);
	}
	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		return BIO_read(psocket_data->io_data.openssl_io.bio, buffer, bufferlen);
	}
	#endif
	else
		return -1;
}

ssize_t psocket_write(struct st_psocket * psocket_data, char * buffer, size_t bufferlen){
	if(psocket_data->io_method == IO_BSD){
		return send(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 0);
	}
	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		return BIO_write(psocket_data->io_data.openssl_io.bio, buffer, bufferlen);
	}
	#endif
	else
		return -1;
}

ssize_t psocket_readline_r(struct st_psocket * psocket_data, char * buffer, ssize_t bufferlen){
	if(psocket_data->io_method == IO_BSD){
		return readline_r(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen, 1);
	}
	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		return BIO_readline_r(psocket_data->io_data.openssl_io.bio, buffer, bufferlen);
	}
	#endif
	else
		return -1;
}
ssize_t psocket_fullsend(struct st_psocket * psocket_data, char * buffer, ssize_t bufferlen){
	if(psocket_data->io_method == IO_BSD){
		return fullsend(psocket_data->io_data.bsd_io.clientfd, buffer, bufferlen);
	}
	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		return BIO_fullsend(psocket_data->io_data.openssl_io.bio, buffer, bufferlen);
	}
	#endif
	else
		return -1;
}



char * psocket_get_client_ip(struct st_psocket * psocket_data){
		return inet_ntoa(psocket_data->io_data.bsd_io.clientaddr.sin_addr);
	}

int psocket_getfd(struct st_psocket * psocket_data){
	
	if(psocket_data->io_method == IO_BSD){
		return psocket_data->io_data.bsd_io.clientfd;
	}

	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		int fd;
		BIO_get_fd( psocket_data->io_data.openssl_io.bio, &fd);
		return fd;
         	}
	#endif
	}

int psocket_bind(struct st_psocket * psocket_data, char * address, int short unsigned port){

	psocket_data->io_method = IO_BSD;

	if( (psocket_data->io_data.bsd_io.serverfd = socket(AF_INET, SOCK_STREAM,0)) == INVAL_SOCK ){
			fprintf(stderr, "psocket_bind * socket() Error!\n");
			perror("socket");
			return -1;
	}
		
	psocket_data->io_data.bsd_io.serveraddr.sin_family = AF_INET;     
	psocket_data->io_data.bsd_io.serveraddr.sin_port = htons(port);  
	psocket_data->io_data.bsd_io.serveraddr.sin_addr.s_addr = inet_addr(address);
	memset(&(psocket_data->io_data.bsd_io.serveraddr.sin_zero), '\0', 8);
	
	if(bind(psocket_data->io_data.bsd_io.serverfd, (struct sockaddr *)&psocket_data->io_data.bsd_io.serveraddr, sizeof(struct sockaddr))  == -1){
			fprintf(stderr, "psocket_bind * bind() Error!\n");
			perror("bind");
			closesocket(psocket_data->io_data.bsd_io.serverfd);
			return -1;
	}

	
	if (listen(psocket_data->io_data.bsd_io.serverfd, 20) == -1) {
			fprintf(stderr, "psocket_bind * listen() Error!\n");
            		perror("listen");
			closesocket(psocket_data->io_data.bsd_io.serverfd);
			return -1;
        }
	return 0;
}


int psocket_set_bsd(struct st_psocket * psocket_data){
	psocket_data->io_data.bsd_io.clientfd = psocket_getfd(psocket_data);
	psocket_data->io_method = IO_BSD;
	}

#ifdef USE_OPENSSL

int psocket_set_openssl(struct st_psocket * psocket_data, char * pem){
	int clientfd = psocket_data->io_data.bsd_io.clientfd;

        psocket_data->io_data.openssl_io.ctx = SSL_CTX_new(SSLv23_server_method());

 	if (!SSL_CTX_use_certificate_file( psocket_data->io_data.openssl_io.ctx,pem,SSL_FILETYPE_PEM)
            || !SSL_CTX_use_PrivateKey_file(psocket_data->io_data.openssl_io.ctx,pem,SSL_FILETYPE_PEM)
            || !SSL_CTX_check_private_key(psocket_data->io_data.openssl_io.ctx)) {

       		 fprintf(stderr, "Error setting up SSL_CTX!! (portable_socket.h)\n");
       		 ERR_print_errors_fp(stderr);
       		 return -1;
 	}

 	/* New SSL BIO setup as server */
 	psocket_data->io_data.openssl_io.sbio = BIO_new_ssl(psocket_data->io_data.openssl_io.ctx,0);

 	BIO_get_ssl(psocket_data->io_data.openssl_io.sbio, &psocket_data->io_data.openssl_io.ssl);
 	SSL_set_fd(psocket_data->io_data.openssl_io.ssl, clientfd);

 	if(!psocket_data->io_data.openssl_io.ssl) {
   		 fprintf(stderr, "Can't locate SSL pointer! (portable_socket.h)\n");
       		 return -1;
 	}

 	/* Don't want any retries */
 	SSL_set_mode(psocket_data->io_data.openssl_io.ssl, SSL_MODE_AUTO_RETRY);
 	psocket_data->io_data.openssl_io.bio = psocket_data->io_data.openssl_io.sbio;

	psocket_data->io_method = IO_OPENSSL;

	return 0;
}

#endif

int psocket_copy(struct st_psocket * new_psocket, struct st_psocket * old_psocket){

	memcpy(new_psocket, old_psocket, sizeof(struct st_psocket));
        new_psocket->io_method  = old_psocket->io_method;

	#ifdef USE_OPENSSL
	if(new_psocket->io_method == IO_OPENSSL)
        	new_psocket->io_data.openssl_io.bio  = BIO_pop(old_psocket->io_data.openssl_io.bio);
	#endif

	return 0;
}

int psocket_accept(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD){
	socklen_t sin_size = sizeof(struct sockaddr_in);
		if (   (psocket_data->io_data.bsd_io.clientfd = accept(psocket_data->io_data.bsd_io.serverfd, (struct sockaddr *)&psocket_data->io_data.bsd_io.clientaddr, &sin_size)) == -1) {
                	//perror("accept");
                	return -1;
		}
	}

	/*OpenSSL bind removed.  Impossible to find IP address otherwise.
          If using OpenSSL run psocket_set_openssl shortly after after accepting
          a connection*/

	return 0;
}

int psocket_connect(struct st_psocket * psocket_data, char * host, int unsigned short port){
	if(psocket_data->io_method == IO_BSD){
		if( (psocket_data->io_data.bsd_io.clientfd = socket(AF_INET, SOCK_STREAM,0)) == INVAL_SOCK ){
			fprintf(stderr, "psocket_connect * socket() Error!\n");
			perror("socket");
			return -1;
		}
       
		psocket_data->io_data.bsd_io.clientaddr.sin_family = AF_INET;        /* host byte order */
        	psocket_data->io_data.bsd_io.clientaddr.sin_port = htons(port); /* short, network byte order */


		struct hostent host_ent_r;
		struct hostent * host_ent;
		char ip_buf[100];
		#ifdef WIN32
		  if((host_ent = gethostbyname(host)) != NULL){
		#else
		  if(gethostbyname_r(host, &host_ent_r, ip_buf, 100, &host_ent, &errno) == 0){
		#endif
			#ifdef WIN32
				struct in_addr in;
				memcpy (&in, host_ent->h_addr, sizeof (struct in_addr));
 				strlcpy(ip_buf, inet_ntoa(in), 100);
			#else
 				inet_ntop(PF_INET, host_ent -> h_addr, ip_buf, 100);
			#endif
		}
		else
			strlcpy(ip_buf, host, 100);
	
       		psocket_data->io_data.bsd_io.clientaddr.sin_addr.s_addr = inet_addr(ip_buf);    
                memset(&(psocket_data->io_data.bsd_io.clientaddr.sin_zero), '\0', 8); // zero the rest of the struct
	       
		if( connect(psocket_data->io_data.bsd_io.clientfd, (LPSOCKADDR)&psocket_data->io_data.bsd_io.clientaddr, sizeof(SOCKADDR_IN)) == -1 ){
			fprintf(stderr, "psocket_connect * connect() Error!\n");
			perror("connect");
			return -1;
		}
	}

	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
 		psocket_data->io_data.openssl_io.ctx = SSL_CTX_new(SSLv23_client_method());
// 
//  		if (!SSL_CTX_check_private_key(psocket_data->io_data.openssl_io.ctx)) {
// 			fprintf(stderr, "Error setting up SSL_CTX\n");
// 			ERR_print_errors_fp(stderr);
// 			return -1;
// 		}

        	psocket_data->io_data.openssl_io.sbio = BIO_new_ssl_connect(psocket_data->io_data.openssl_io.ctx);

        	BIO_get_ssl(psocket_data->io_data.openssl_io.sbio, &psocket_data->io_data.openssl_io.ssl);

        	if(!psocket_data->io_data.openssl_io.ssl) {
			   fprintf(stderr, "Can't locate SSL pointer\n");
			   return -1;
        	}

        	/* Don't want any retries */
		    SSL_set_mode(psocket_data->io_data.openssl_io.ssl, SSL_MODE_AUTO_RETRY);

        	/* We might want to do other things with ssl here */


	
		    char host_str[30];
		    snprintf(host_str, 30, "%s:%d", host, port);

        	 BIO_set_conn_hostname(psocket_data->io_data.openssl_io.sbio, host_str);

        	if(BIO_do_connect(psocket_data->io_data.openssl_io.sbio) <= 0) {
                		fprintf(stderr, "Error connecting to server\n");
                		ERR_print_errors_fp(stderr);
		            	return -1;
        		}

        	if(BIO_do_handshake(psocket_data->io_data.openssl_io.sbio) <= 0) {
                		fprintf(stderr, "Error establishing SSL connection\n");
                		ERR_print_errors_fp(stderr);
		            	return -1;
       		 }
		psocket_data->io_data.openssl_io.bio = psocket_data->io_data.openssl_io.sbio;
	}
	#endif	
	else
		return -1;

	return 0;
}


int psocket_starttls(struct st_psocket * psocket_data){

	#ifdef USE_OPENSSL
	if(psocket_data->io_method == IO_BSD){
 		psocket_data->io_data.openssl_io.ctx = SSL_CTX_new(SSLv23_client_method());

        	psocket_data->io_data.openssl_io.sbio = BIO_new_ssl_connect(psocket_data->io_data.openssl_io.ctx);
		
        	
		BIO_get_ssl(psocket_data->io_data.openssl_io.sbio, &psocket_data->io_data.openssl_io.ssl);
		SSL_set_fd(psocket_data->io_data.openssl_io.ssl, psocket_data->io_data.bsd_io.clientfd);

        	if(!psocket_data->io_data.openssl_io.ssl) {
			   fprintf(stderr, "Can't locate SSL pointer\n");
			   return -1;
        	}

        	/* Don't want any retries */
		    SSL_set_mode(psocket_data->io_data.openssl_io.ssl, SSL_MODE_AUTO_RETRY);

        	if(BIO_do_handshake(psocket_data->io_data.openssl_io.sbio) <= 0) {
                		fprintf(stderr, "Error establishing SSL connection\n");
                		ERR_print_errors_fp(stderr);
		            	return -1;
       		 }
		psocket_data->io_data.openssl_io.bio = psocket_data->io_data.openssl_io.sbio;
		psocket_data->io_method = IO_OPENSSL;
	}
	#endif	

	return 0;
}

void psocket_shutdown(struct st_psocket * psocket_data){
	psocket_close_client(psocket_data);
	psocket_close_server(psocket_data);
}

void psocket_close_server(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD)
		closesocket(psocket_data->io_data.bsd_io.serverfd);

	/*SSL we only close the client, and a psocket_free takes care of server stuff
         *Both need to be called anyway*/
}
void psocket_close_client(struct st_psocket * psocket_data){
	if(psocket_data->io_method == IO_BSD)
		closesocket(psocket_data->io_data.bsd_io.clientfd);

	#ifdef USE_OPENSSL
	else if(psocket_data->io_method == IO_OPENSSL){
		if(psocket_data->io_data.openssl_io.sbio != NULL)
        		BIO_free_all(psocket_data->io_data.openssl_io.sbio); /*Accept/Client BIO*/
		psocket_data->io_data.openssl_io.sbio = NULL;
	}
	#endif	
}

